<?php

namespace App\Kuafu\Order\Service;

use App\Kuafu\Common\Exceptions\CommonException;
use App\Kuafu\Common\Service\SeparateService;
use App\Kuafu\CouponPackage\Model\CouponPackageShopMappingModel;
use App\Kuafu\Guanyuan\Model\ShopDailyWaiMaiRateModel;
use App\Kuafu\Order\Enum\CodeEnum;
use App\Kuafu\Order\Enum\OrderCacheEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Enum\OrderMerchantEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\Order\Jobs\OrderMerchantDelayCache;
use App\Kuafu\Order\Model\OrderPayUnionSplitModel;
use App\Kuafu\Product\Enum\ProductCodeEnum;
use App\Kuafu\Product\Exceptions\ProductException;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;


/**
 * 商家APP订单统计服务-use
 */
class OrderMerchantUseService extends OrderMerchantBaseService
{
    private array $showTimeTabInfo, $compareTimeTabInfo, $overviewCompareInfo;

    private int $showTimeTab;

    private int $compareTimeTab;

    private array $useShopIds;

    public function __construct(array $shopIds)
    {
        $this->useShopIds = $shopIds;
    }

    /**
     * 设置查看的时间选项卡
     * @return $this
     * @throws \App\Kuafu\Order\Exceptions\OrderException
     */
    public function setShowTimeTab(int $showTimeTab, int $showStartAt, int $showEndAt)
    {
        $this->showTimeTab = $showTimeTab;

        $timeObj                   = OrderMerchantEnum::getIns()
                                                      ->setShowTimeTab($showTimeTab, $showStartAt, $showEndAt);
        $this->overviewCompareInfo = $timeObj->getOrderOverviewCompareInfo();
        $this->showTimeTabInfo     = $timeObj->getShowTimeTab();

        if ($showTimeTab == OrderMerchantEnum::SHOW_TIME_TAB_DEFINE) {
            if ($showEndAt - $showStartAt > 86400 * 31) {
                throw new OrderException(OrderCodeEnum::ERR_SHOW_TIME_DURATION);
            }
        }

        // 对比的时间戳，跨了隔断日期，强制用clickhouse
        try {
            (new SeparateService($this->overviewCompareInfo[0][1], $this->overviewCompareInfo[0][2]))->check();
        } catch (CommonException $e) {
            $this->setForceEngine(SeparateService::CLICKHOUSE);
        }

        return $this;
    }

    /**
     * 支付方式 - 聚合统计
     *
     * @return mixed
     */
    public function groupStatistics()
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        return [
            "data" => $this->groupStatisticsBase(),
            "showStartAt" => $this->showTimeTabInfo[1],
            "showEndAt" => $this->showTimeTabInfo[2]
        ];
    }

    /**
     * 设置对比的时间选项卡
     * @return $this
     * @throws \App\Kuafu\Order\Exceptions\OrderException
     */
    public function setCompareTimeTab(int $compareTimeTab, int $compareStartAt, int $compareEndAt)
    {
        $this->compareTimeTab = $compareTimeTab;

        $this->compareTimeTabInfo = OrderMerchantEnum::getIns()
                                                     ->setCompareTimeTab($compareTimeTab, $compareStartAt,
                                                         $compareEndAt)
                                                     ->getCompareTimeTab();

        // 对比的时间戳，跨了隔断日期，强制用clickhouse
        try {
            (new SeparateService($this->compareTimeTabInfo[1], $this->compareTimeTabInfo[2]))->check();
        } catch (CommonException $e) {
            $this->setForceEngine(SeparateService::CLICKHOUSE);
        }

        return $this;
    }

    // 订单信息对比数据
    private function orderUseCompare(array $data1, array $data2)
    {
        $res = [];
        foreach ($data1 as $statisticsType => $num) {
            $numType = OrderMerchantEnum::STAT_TO_NUM[$statisticsType];
            if ($numType == OrderMerchantEnum::NUM_TYPE_PERCENT) {
                $changeNum = (float)($data2[$statisticsType] != 0 ? bcdiv($num, $data2[$statisticsType], 2) : 0);
            } else {
                $changeNum = $num - $data2[$statisticsType];

                if (!is_int($changeNum)) {
                    $changeNum = round($changeNum, 2);
                }
            }

            $res[] = [
                'statisticsType' => $statisticsType,
                'numType'        => $numType,
                'name'           => OrderMerchantEnum::STAT_TEXT[$statisticsType],
                'num'            => $num,
                'changeNum'      => $changeNum,
                'changeRate'     => OrderMerchantBaseService::formatPercent($changeNum, $data2[$statisticsType]),
            ];
        }

        return $res;
    }

    // 券包订单
    private function couponPackageOrderUse()
    {
        // 当前的
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $data1 = $this->couponPackageOrderBase();

        // 对比的
        $this->setShopStartEndTime($this->useShopIds, $this->compareTimeTabInfo[1], $this->compareTimeTabInfo[2]);
        $data2 = $this->couponPackageOrderBase();

        return $this->orderUseCompare($data1, $data2);
    }

    // 店内订单
    private function inOrderUse()
    {
        // 当前的
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $data1 = $this->inOrderBase();

        // 对比的
        $this->setShopStartEndTime($this->useShopIds, $this->compareTimeTabInfo[1], $this->compareTimeTabInfo[2]);
        $data2 = $this->inOrderBase();

        return $this->orderUseCompare($data1, $data2);
    }

    // 外卖订单
    private function waiMaiOrderUse(int $sourceType = 0)
    {
        // 当前的
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $data1 = $this->waiMaiOrderBase($sourceType);

        // 对比的
        $this->setShopStartEndTime($this->useShopIds, $this->compareTimeTabInfo[1], $this->compareTimeTabInfo[2]);
        $data2 = $this->waiMaiOrderBase($sourceType);

        return $this->orderUseCompare($data1, $data2);
    }

    // 营业额
    public function originalPrice(): array
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        return $this->originalPriceBase();
    }

    /**
     * 首页数据信息
     */
    public function indexData(int $vipIn)
    {
//        $indexCacheKey = OrderCacheEnum::getOrderMerchantIndex($this->useShopIds[0], $this->showTimeTabInfo[1],
//            $this->showTimeTabInfo[2], $this->compareTimeTabInfo[1], $this->compareTimeTabInfo[3]);

//        return
//            Cache::remember($indexCacheKey, OrderCacheEnum::ORDER_MERCHANT_INDEX_TTL, function () {

        $data = [

            // 营业构成统计
            'businessForm'        => [
                'inOrder'            => $inOrder = $this->inOrderUse(),
                'waiMaiOrder'        => $waiMaiOrder = $this->waiMaiOrderUse(),
                'couponPackageOrder' => $couponPackageOrder = $this->couponPackageOrderUse(),
                'compareImg'         => [
                    [
                        'text'      => '店内实收',
                        'orderType' => OrderMerchantEnum::ORDER_TYPE_IN,
                        'num'       => $inIncomePrice = collect($inOrder)
                            ->where('statisticsType', OrderMerchantEnum::STAT_INCOME_PRICE)
                            ->first()['num'],
                        'rate'      => &$inIncomeRate,
                    ],
                    [
                        'text'      => '外卖实收',
                        'orderType' => OrderMerchantEnum::ORDER_TYPE_WAI_MAI,
                        'num'       => $waiMaiIncomePrice = collect($waiMaiOrder)
                            ->where('statisticsType', OrderMerchantEnum::STAT_INCOME_PRICE)
                            ->first()['num'],
                        'rate'      => &$waiMaiIncomeRate,
                    ],
                    [
                        'text'      => '会员收入',
                        'orderType' => OrderMerchantEnum::ORDER_TYPE_COUPON_PACKAGE,
                        'num'       => $couponPackageIncomePrice = collect($couponPackageOrder)
                            ->where('statisticsType', OrderMerchantEnum::STAT_INCOME_PRICE)
                            ->first()['num'],
                        'rate'      => &$couponPackageIncomeRate,
                    ]
                ],
            ],

            // 营业概览
            'businessOverview'    => [
                'timeInfo'   => [
                    'showStartAt'    => $this->showTimeTabInfo[1],
                    'showEndAt'      => $this->showTimeTabInfo[2],
                    'compareStartAt' => $this->compareTimeTabInfo[1],
                    'compareEndAt'   => $this->compareTimeTabInfo[2],
                ],
                'statistics' => &$statistics,
            ],
            // 店铺是否绑定券包
            'isBindCouponPackage' => $this->checkCouponPackage(),

            // 数据更新时间
            'dataUpdateTime'      => time(),

            // 外卖评分异常
            'waiMaiScoreError' => $this->waiMaiScoreError(),
        ];

        $statistics = [];

        // 营业构成的比例
        $totalIncome = $inIncomePrice + $waiMaiIncomePrice + $couponPackageIncomePrice;
        if ($totalIncome > 0) {
            $lastIncomeRate = 100;

            // 正序排，最后一个用减法
            $ascTypes = collect([
                OrderMerchantEnum::ORDER_TYPE_IN             => $inIncomePrice,
                OrderMerchantEnum::ORDER_TYPE_WAI_MAI        => $waiMaiIncomePrice,
                OrderMerchantEnum::ORDER_TYPE_COUPON_PACKAGE => $couponPackageIncomePrice,
            ])->sort();

            $k = 1;
            foreach ($ascTypes as $statisticsType => $price) {
                if ($k == $ascTypes->count()) {
                    $rate = $lastIncomeRate;
                } else {
                    $rate = (float)bcdiv($price, $totalIncome, 4);
                    $rate = (float)bcmul($rate, 100, 2);
                }

                $lastIncomeRate = bcsub($lastIncomeRate, $rate, 2);
                $k++;

                $ascTypes[$statisticsType] = $rate;
            }

            $inIncomeRate            = (float)$ascTypes[OrderMerchantEnum::ORDER_TYPE_IN];
            $waiMaiIncomeRate        = (float)$ascTypes[OrderMerchantEnum::ORDER_TYPE_WAI_MAI];
            $couponPackageIncomeRate = (float)$ascTypes[OrderMerchantEnum::ORDER_TYPE_COUPON_PACKAGE];

        } else {
            $inIncomeRate = $waiMaiIncomeRate = $couponPackageIncomeRate = 0;
        }

        // 列出所有需要统计的订单类型
        $mKeysArr = [
            $inOrder,
            $waiMaiOrder,
            $vipIn ? $couponPackageOrder : [],
        ];

        // 累加需要统计订单类型的项
        $mTotalCalc = function (int $statisticsType, string $column) use ($mKeysArr) {
            $s = 0;
            foreach ($mKeysArr as $key) {
                $key = array_column($key, null, 'statisticsType');
                $s   += ($key[$statisticsType][$column] ?? 0);
            }

            return $s;
        };

        // 营业概览 = 店内 + 店外
        foreach (OrderMerchantEnum::STAT_INDEX_OVERVIEW as $statisticsType) {

            if ($statisticsType == OrderMerchantEnum::STAT_AFTER_DISCOUNT_AVG_PRICE) {

                // 当前的订单实收 / 有效订单
                $incomePrice = $mTotalCalc(OrderMerchantEnum::STAT_INCOME_PRICE, 'num');
                $orderCount  = $mTotalCalc(OrderMerchantEnum::STAT_ORDER_COUNT, 'num');

                $num = $orderCount != 0 ? floor($incomePrice / $orderCount) : 0;

                // 对比的订单实收 / 有效订单
                $incomePrice = $mTotalCalc(OrderMerchantEnum::STAT_INCOME_PRICE,
                        'num') - $mTotalCalc(OrderMerchantEnum::STAT_INCOME_PRICE, 'changeNum');

                $orderCount = $mTotalCalc(OrderMerchantEnum::STAT_ORDER_COUNT,
                        'num') - $mTotalCalc(OrderMerchantEnum::STAT_ORDER_COUNT, 'changeNum');

                $lastNum = $orderCount != 0 ? floor($incomePrice / $orderCount) : 0;

                $changeNum = $num - $lastNum;

            } else {
                $num       = $mTotalCalc($statisticsType, 'num');
                $changeNum = $mTotalCalc($statisticsType, 'changeNum');
            }

            $changeRate = OrderMerchantBaseService::formatPercent($changeNum, $num - $changeNum);

            $statistics[] = [
                'statisticsType' => $statisticsType,
                'numType'        => OrderMerchantEnum::STAT_TO_NUM[$statisticsType],
                'name'           => OrderMerchantEnum::STAT_TEXT[$statisticsType],
                'num'            => $num,
                'changeNum'      => $changeNum,
                'changeRate'     => $changeRate
            ];
        }

//            OrderMerchantDelayCache::dispatch(
//                OrderMerchantDelayCache::CACHE_TYPE_INDEX,
//                [
//                    'shopId'         => $this->useShopIds[0],
//                    'showTimeTab'    => $this->showTimeTab,
//                    'showStartAt'    => $this->showTimeTabInfo[1],
//                    'showEndAt'      => $this->showTimeTabInfo[2],
//                    'compareTimeTab' => $this->compareTimeTab,
//                    'compareStartAt' => $this->compareTimeTabInfo[1],
//                    'compareEndAt'   => $this->compareTimeTabInfo[2],
//                    'orderType'      => OrderMerchantEnum::ORDER_TYPE_TOTAL,
//                ]
//            )->onQueue(OrderMerchantDelayCache::QUEUE_NAME);

        return $data;
//        });
    }

    /**
     * 外卖异常评分
     * @return array
     */
    private function waiMaiScoreError(): array
    {
        if ($this->showTimeTab != OrderMerchantEnum::SHOW_TIME_TAB_TODAY) {
            return [];
        }

        // 查询今日美团、饿了么评分
        $today = ShopDailyWaiMaiRateModel::query()
                                         ->where('rmsShopId', $this->useShopIds[0])
                                         ->where('date', date('Y-m-d'))
                                         ->get();

        $errors = [];

        $verifySourceTypes = [WaiMaiDBEnum::SOURCE_TYPE_MT_WM, WaiMaiDBEnum::SOURCE_TYPE_ELM];
        foreach ($verifySourceTypes as $sourceType) {

            // 今日评分
            $todayScore = $today->where('type', $sourceType)->first()['rating'] ?? 0;

            // 今日评分<阈值
            if ($todayScore < OrderMerchantEnum::WAIMAI_SCORE_ALERT_THRESHOLD && $todayScore > 0) {

                // 昨日评分
                $yesterday = ShopDailyWaiMaiRateModel::query()
                                                     ->where('rmsShopId', $this->useShopIds[0])
                                                     ->where('date', date('Y-m-d', strtotime('-1 day')))
                                                     ->get();

                $yesterdayScore = $yesterday->where('type', $sourceType)->first()['rating'] ?? 0;

                // 较昨日评分变化
                if ($yesterdayScore != $todayScore) {

                    $errors[] = [
                        'sourceType'     => $sourceType,
                        'sourceTypeText' => WaiMaiDBEnum::SOURCE_TYPE_TEXT[$sourceType],
                        'score'          => ShopDailyWaiMaiRateModel::formatScore($todayScore),
                        'yesterdayScore' => ShopDailyWaiMaiRateModel::formatScore($yesterdayScore),
                    ];
                }
            }
        }

        return $errors;
    }

    /**
     * 订单明细 - 店内店外订单概览
     * @param int $orderType
     * @return array
     * @throws OrderException
     */
    public function orderOverview(int $orderType, int $sourceType): array
    {
        $orderCacheKey = OrderCacheEnum::getOrderMerchantOrders($this->useShopIds[0], $this->showTimeTabInfo[1],
            $this->showTimeTabInfo[2], $orderType);

//        return Cache::remember($orderCacheKey, OrderCacheEnum::ORDER_MERCHANT_ORDERS_TTL, function () use ($orderType) {

        $statistics = [];

        foreach ($this->overviewCompareInfo as $compareK => $compareV) {

            // 逐个获取统计信息，并对比
            $order = $this->setCompareTimeTab(OrderMerchantEnum::COMPARE_TIME_TAB_DURATION_DAY, $compareV[1],
                $compareV[2]);

            // 店内、店外订单处理
            if ($orderType == OrderMerchantEnum::ORDER_TYPE_IN) {
                $order = $order->inOrderUse();
            } elseif ($orderType == OrderMerchantEnum::ORDER_TYPE_WAI_MAI) {
                $order = $order->waiMaiOrderUse($sourceType);
            } elseif ($orderType == OrderMerchantEnum::ORDER_TYPE_DISTRIBUTION) {
                $statistics = $order->inDistributionOrderUse();
                break;
            } else {
                throw new OrderException(OrderCodeEnum::ERR_ORDER_IN_OUT_TYPE);
            }

            foreach ($order as $orderK => $orderV) {

                if ($compareK == 0) {
                    $statistics[$orderK] = [
                        'name'           => OrderMerchantEnum::STAT_TEXT[$orderV['statisticsType']],
                        'statisticsType' => $orderV['statisticsType'],
                        'numType'        => OrderMerchantEnum::STAT_TO_NUM[$orderV['statisticsType']] ?? OrderMerchantEnum::NUM_TYPE_PRICE,
                        'num'            => $orderV['num'],
                    ];
                }

                $statistics[$orderK]['compare'][$compareK] = [
                    'text'       => $compareV[0],
                    'changeNum'  => $orderV['changeNum'],
                    'changeRate' => $orderV['changeRate'],
                ];
            }
        }

        $data = [
            'statistics'     => $statistics,

            // 数据更新时间
            'dataUpdateTime' => time(),
            'showStartDate'  => date('Y-m-d', $this->showTimeTabInfo[1]),
            'showEndDate'    => date('Y-m-d', $this->showTimeTabInfo[2]),
        ];
//
//            OrderMerchantDelayCache::dispatch(
//                OrderMerchantDelayCache::CACHE_TYPE_ORDER,
//                [
//                    'shopId'      => $this->useShopIds[0],
//                    'showTimeTab' => $this->showTimeTab,
//                    'showStartAt' => $this->showTimeTabInfo[1],
//                    'showEndAt'   => $this->showTimeTabInfo[2],
//                    'orderType'   => OrderMerchantEnum::ORDER_TYPE_TOTAL,
//                ]
//            )->onQueue(OrderMerchantDelayCache::QUEUE_NAME);
//
        return $data;
//        });
    }

    /**
     * 订单明细 - 店内店外折线图
     * @param int $statisticsType
     * @param int $orderType
     * @return array
     * @throws OrderException
     */
    public function orderCharts(int $statisticsType, int $orderType, int $sourceType = 0, int $vipIn = 0): array
    {
        if ($orderType == OrderMerchantEnum::ORDER_TYPE_TOTAL) {
            $timeList = array_merge([$this->showTimeTabInfo], [$this->compareTimeTabInfo]);
        } else {
            $timeList = array_merge([$this->showTimeTabInfo], $this->overviewCompareInfo);
        }

        $interval = OrderMerchantEnum::getChartInterval($this->showTimeTab, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);

        // 当前的list长度
        $currentListLength = 0;

        $data = [];
        foreach ($timeList as $k => $time) {

            if ($k == 0) {

                // 当前的 - 取同时段对比
                $endTime = $time[2];
            } else {

                // 对比的 - 取完整的时间
                $endTime = $time[3];
            }

            $lists = $this->setShopStartEndTime($this->useShopIds, 0, 0)
                          ->chartsBase($time[1], $endTime, $time[3], $interval, $statisticsType, $orderType,
                              $this->showTimeTab, $sourceType, $vipIn);

            // 控制当前的list长度，是最长的
            if ($k == 0) {
                $currentListLength = count($lists);
            } else {

                if (count($lists) > $currentListLength) {
                    $lists = array_slice($lists, 0, count($lists) - 1);
                }
            }

            $data[] = [
                'text'           => $time[0],
                'statisticsType' => $statisticsType,
                'numType'        => OrderMerchantEnum::STAT_TO_NUM[$statisticsType] ?? OrderMerchantEnum::NUM_TYPE_PRICE,
                'lists'          => $lists,
            ];
        }

        return $data;
    }

    /**
     * 订单列表
     * @param int $orderType
     * @param int $pageSize
     * @param int $pageNum
     * @param int $sourceType
     * @param int $distributionId
     * @return array
     */
    public function orderListsUse(
        int $orderType,
        int $pageSize,
        int $pageNum,
        int $sourceType,
        int $distributionId = 0
    ): array
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);

        if ($orderType == OrderMerchantEnum::ORDER_TYPE_IN) {
            $data = $this->inOrderListsBase($pageSize, $pageNum);
        } elseif ($orderType == OrderMerchantEnum::ORDER_TYPE_WAI_MAI) {
            $data = $this->waiMaiOrderListsBase($pageSize, $pageNum, $sourceType);
        } elseif ($orderType == OrderMerchantEnum::ORDER_TYPE_DISTRIBUTION) {
            $data = $this->inDistributionOrderListsBase($pageSize, $pageNum, $distributionId);
        } else {
            return [];
        }

        $data['timeInfo'] = [
            'showStartAt'   => $this->showTimeTabInfo[1],
            'showEndAt'     => $this->showTimeTabInfo[2],
            'showStartDate' => date('Y-m-d', $this->showTimeTabInfo[1]),
            'showEndDate'   => date('Y-m-d', $this->showTimeTabInfo[2]),
        ];

        return $data;
    }

    /**
     * 支付明细统计 - 列表
     * @param int $payType
     * @param int $payStatus
     * @param int $lastUnionId
     * @param int $limit
     * @param bool $isNew
     * @return mixed
     */
    public function payUnionLists(int $payType, int $payStatus, int $lastUnionId, int $limit, bool $isNew = false)
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        return $this->payUnionListsBase($payType, $payStatus, $lastUnionId, $limit, $isNew);
    }

    /**
     * 支付明细统计 - 列表自定义查询
     * @param int $startTime
     * @param int $endTime
     * @return mixed
     */
    public function payUnionListsDefine(int $startTime, int $endTime)
    {
        $this->setShopStartEndTime($this->useShopIds, $startTime, $endTime);
        return $this->payUnionListsBase(0, 0, 0, 999999);
    }

    /**
     * 会员收入统计 - 列表
     * @param array $couponPackageIds
     * @param int $phone
     * @param int $lastUnionId
     * @param int $limit
     * @return mixed
     */
    public function couponPackageLists(array $couponPackageIds, int $phone, int $lastUnionId, int $limit)
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        return $this->couponPackageListsBase($couponPackageIds, $phone, $lastUnionId, $limit);
    }

    /**
     * 支付明细统计 - 聚合统计数据
     * @param int $payType
     * @param int $payStatus
     * @return array
     */
    public function payUnionListsStatistics(int $payType, int $payStatus): array
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $lists    = $this->payUnionListsStatisticsBase($payType, $payStatus);
        $timeInfo = [
            'showStartAt' => $this->showTimeTabInfo[1],
            'showEndAt'   => $this->showTimeTabInfo[2],
        ];

        return [
            'lists'    => $lists,
            'timeInfo' => $timeInfo,
        ];
    }

    /**
     * 会员收入统计 - 聚合统计数据
     * @param array $couponPackageIds
     * @param int $phone
     * @return array
     */
    public function couponPackageListsStatistics(array $couponPackageIds, int $phone): array
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $lists    = $this->couponPackageListsStatisticsBase($couponPackageIds, $phone);
        $timeInfo = [
            'showStartAt' => $this->showTimeTabInfo[1],
            'showEndAt'   => $this->showTimeTabInfo[2],
        ];

        return [
            'lists'    => $lists,
            'timeInfo' => $timeInfo,
        ];
    }

    /**
     * 支付明细统计 - 支付详情
     * @param int $unionId
     * @return array
     * @throws \App\Kuafu\Pay\Exceptions\PayException
     */
    public function payUnionDetail(int $unionId): array
    {
        $this->setShopStartEndTime($this->useShopIds, 0, 0);
        return $this->payUnionDetailBase($unionId);
    }

    public function couponPackageDetail(int $orderId): array
    {
        $this->setShopStartEndTime($this->useShopIds, 0, 0);
        return $this->couponPackageDetailBase($orderId);
    }

    /**
     * 支付明细统计 - 筛选项枚举
     * @return array
     */
    public static function payUnionListsFilter()
    {
        $arr = [
            'payType'   => collect(OrderPayUnionSplitModel::PAY_TYPE_TEXT)->map(function ($v, $k) {
                return [
                    'text'  => $v,
                    'value' => $k,
                ];
            })->values(),
            'payStatus' => collect(OrderPayUnionSplitModel::PAY_STATUS_TEXT)->map(function ($v, $k) {
                return [
                    'text'  => $v,
                    'value' => $k,
                ];
            })->values(),
        ];

        return $arr;
    }

    /**
     * 会员收入统计 - 筛选项枚举
     * @return array
     */
    public function couponPackageListsFilter()
    {
        $data              = $this->getCouponPackageNameByOrder($this->useShopIds[0]);
        $couponPackageInfo = [];
        if ($data->count()) {
            foreach ($data as $k => $list) {
                $couponPackageInfo[] = [
                    'id'   => $k,
                    'name' => $list
                ];
            }
        }
        return $couponPackageInfo;
    }

    /**
     * 堂食、外卖的菜品销售统计
     * @param int $orderType
     * @param array $filterIds
     * @param int $groupByType
     * @param int $orderByColumn
     * @param string $sort
     * @return mixed
     * @throws OrderException
     * @throws ProductException
     */
    private function productStatisticsDoBase(
        int $orderType,
        array $filterIds,
        int $groupByType,
        int $orderByColumn,
        string $sort,
        int $sourceType = 0
    ) {
        $orderBy = OrderMerchantEnum::PRODUCT_SALES_COLUMN[$orderByColumn] ?? '';
        if (!$orderBy) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_SALES_PARAM, '不存在的orderByColumn');
        }

        if ($sort == 'asc') {
            $sortFunc = "sortBy";
        } else {
            $sortFunc = "sortByDesc";
        }

        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);

        switch ($orderType) {
            case OrderMerchantEnum::ORDER_TYPE_IN:
                $lists = $this->inOrderProductStatisticsBase($filterIds, $groupByType);
                break;
            case OrderMerchantEnum::ORDER_TYPE_WAI_MAI:
                $lists = $this->waiMaiOrderProductStatisticsBase($filterIds, $groupByType, $sourceType);
                break;
            default:
                throw new OrderException(OrderCodeEnum::ERR_ORDER_IN_OUT_TYPE);

        }

        // 先按正序排，方便计算百分比
//        $lists = $lists->sortBy($orderBy);

        // 循环计算百分比，最后一个用减法
        $sumSalesNum   = $lists->sum("salesNum");
        $sumIncomes    = $lists->sum("incomes");
        $lastSalesRate = $lastIncomesRate = 1;

        $k          = 1;
        $countLists = $lists->count();
        return $lists->map(function ($item) use (
            &$k,
            $countLists,
            $sumSalesNum,
            $sumIncomes,
            &$lastSalesRate,
            &$lastIncomesRate
        ) {
            if ($countLists == $k) {
                $item['salesNumRate'] = (float)($item['salesNum'] > 0 ? $lastSalesRate : 0);
                $item['incomesRate']  = (float)($item['incomes'] > 0 ? $lastIncomesRate : 0);
            } else {
                $item['salesNumRate'] = $sumSalesNum > 0 ? (float)bcdiv($item['salesNum'], $sumSalesNum, 4) : 0;
                $item['incomesRate']  = $sumIncomes > 0 ? (float)bcdiv($item['incomes'], $sumIncomes, 4) : 0;
            }

            $item['salesNum'] = (int)$item['salesNum'];
            $item['incomes']  = (int)$item['incomes'];

            $k++;
            $lastSalesRate   = bcsub($lastSalesRate, $item['salesNumRate'], 4);
            $lastIncomesRate = bcsub($lastIncomesRate, $item['incomesRate'], 4);

            return $item;
        })
                     ->$sortFunc($orderBy)
                     ->values();
    }

    /**
     * 外卖 - 菜品销售统计
     * @param array $filterNames
     * @param int $groupByType
     * @param int $orderByColumn
     * @param string $sort
     * @return mixed
     * @throws ProductException
     */
    public function waiMaiOrderProductStatisticsUse(
        array $filterNames,
        int $groupByType,
        int $orderByColumn,
        string $sort,
        int $sourceType
    ) {
        if ($groupByType != OrderMerchantEnum::PRODUCT_SALES_GROUP_BY_PRODUCT) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_SALES_GROUP_BY_TYPE, '外卖仅能按照菜品分组');
        }

        $lists = $this->productStatisticsDoBase(
            OrderMerchantEnum::ORDER_TYPE_WAI_MAI,
            $filterNames,
            $groupByType,
            $orderByColumn,
            $sort,
            $sourceType
        )->map(function ($item) {
            unset($item->orderRel);
            $item['sourceTypeText'] = CodeEnum::SOURCE_TYPES[$item['sourceType']] ?? '';
            return $item;
        });

        $timeInfo = [
            'showStartAt' => $this->showTimeTabInfo[1],
            'showEndAt'   => $this->showTimeTabInfo[2],
        ];

        return [
//            'total' => [
//                'incomes' => $lists->sum('incomes'),
//            ],
            'lists'    => $lists,
            'timeInfo' => $timeInfo,
        ];
    }

    /**
     * 堂食 - 菜品销售统计
     * @param array $filterIds
     * @param int $groupByType
     * @param int $orderByColumn
     * @param string $sort
     * @return mixed
     * @throws ProductException
     */
    public function inOrderProductStatisticsUse(array $filterIds, int $groupByType, int $orderByColumn, string $sort)
    {
        if (!in_array($groupByType, array_keys(OrderMerchantEnum::PRODUCT_SALES_GROUP_BY_ENUM))) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_SALES_GROUP_BY_TYPE);
        }

        $lists = $this->productStatisticsDoBase(
            OrderMerchantEnum::ORDER_TYPE_IN,
            $filterIds,
            $groupByType,
            $orderByColumn,
            $sort
        )->map(function ($item) {
            unset($item->orderRel);
            return $item;
        });

        $timeInfo = [
            'showStartAt' => $this->showTimeTabInfo[1],
            'showEndAt'   => $this->showTimeTabInfo[2],
        ];

        return [
            'lists'    => $lists,
            'timeInfo' => $timeInfo,
        ];
    }


    /**
     * 支付方式 - 聚合统计
     *
     * @return mixed
     */
    public function groupStatisticsNew()
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $data = $this->groupStatisticsBaseNew();
        return [
            "data"        => $data['data'],
            "total"       => $data['total'],
            "showStartAt" => $this->showTimeTabInfo[1],
            "showEndAt"   => $this->showTimeTabInfo[2]
        ];
    }

    /**
     * 支付明细统计 - 聚合统计数据
     * @param int $payType
     * @param int $payStatus
     * @return array
     */
    public function payUnionListsStatisticsNew(int $payType, int $payStatus): array
    {
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        $lists    = $this->payUnionListsStatisticsBaseNew($payType, $payStatus);
        $timeInfo = [
            'showStartAt' => $this->showTimeTabInfo[1],
            'showEndAt'   => $this->showTimeTabInfo[2],
        ];

        return [
            'list'     => $lists['list'],
            'total'    => $lists['total'],
            'timeInfo' => $timeInfo,
        ];
    }

    // 分销订单
    private function inDistributionOrderUse()
    {
        // 当前的
        $this->setShopStartEndTime($this->useShopIds, $this->showTimeTabInfo[1], $this->showTimeTabInfo[2]);
        return $this->inDistributionOrderBase();
    }
}

